<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>

    <link rel="stylesheet" type="text/css" 
      href="/assets/css/straybirds.css" media="screen" />
    <link rel="stylesheet" type="text/css" 
      href="/assets/css/pygments.css" media="screen" />

    <!-- MathJax Section Start -->

    <script type="text/javascript"
    src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
    </script>
    <script>
        MathJax.Hub.Config({
              tex2jax: {
              skipTags: ['script', 'noscript', 'style', 'textarea', 'pre']
              }
        });
        MathJax.Hub.Queue(function() {
            var all = MathJax.Hub.getAllJax(), i;
            for(i=0; i < all.length; i += 1) {
                all[i].SourceElement().parentNode.className += ' has-jax';
            }
        });
    </script>

    <!-- MathJax Section End -->

    <!-- Google Analytics Start-->
    <script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-48100787-1', 'minixalpha.github.io');
  ga('send', 'pageview');

</script>

    <!-- Google Analytics End -->

    <title>wsgiref 源代码分析</title>
  </head>

  <body>
    <div class="container">
      <header>
        <div class="container">
          <h1>
              <a href="/" title="Home Page"> 潇湘夜雨 </a>
          <span class="github-src">
            <a href ="https://github.com/minixalpha/minixalpha.github.io"
               target="_blank"
               title="Fork me on GitHub">
              <img src="/assets/images/GitHub-Mark-Light-32px.png" alt="">
            </a>
          </span>
          </h1>
        </div>
      </header>

      <aside id="left-side">
        <h2> 分类 </h2>
  <ul class="category-list">
      
            
                <li style="background-color: #444">
                <a href="/categories/源代码阅读"> 源代码阅读 (20) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/设计模式"> 设计模式 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/科研"> 科研 (6) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/思想"> 思想 (2) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/技术"> 技术 (18) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/工具"> 工具 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/虚拟机"> 虚拟机 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/java"> java (11) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/c语言"> c语言 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/读书"> 读书 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/操作系统"> 操作系统 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/英语"> 英语 (10) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/计算机系统"> 计算机系统 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/生活"> 生活 (1) </a>
                </li>
            
      
  </ul>

      </aside>

      <aside id="right-side">
        <h2> 归档 </h2>
  <ul class="archive-list">
    
    
    
        
        
        
        
            
            <li>
                <a href="/2014/08">
                    2014-08 (1)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/07">
                    2014-07 (5)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/05">
                    2014-05 (12)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/04">
                    2014-04 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/03">
                    2014-03 (11)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/02">
                    2014-02 (6)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/01">
                    2014-01 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/11">
                    2013-11 (10)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/10">
                    2013-10 (3)
                </a>
            </li>

        
        
    
        
        
        
        
            
            <li>
                <a href="/2010/09">
                    2010-09 (1)
                </a>
            </li>

        
        
    
  </ul>

      </aside>

      <article>

<h1>wsgiref 源代码分析</h1>

<h2>wsgiref</h2>

<p><a href="http://docs.python.org/2/library/wsgiref.html">wsgiref</a> 是Python标准库给出的 <a href="http://www.python.org/dev/peps/pep-0333/">WSGI</a> 的参考实现。</p>

<p>WSGI是Python Web 开发中为服务器程序和应用程序设定的标准，满足这一标准的服务器程序和应用程序可以配合使用。我在上一篇博文《<a href="http://blog.csdn.net/on_1y/article/details/18803563">WSGI简介</a>》中对此有详细的介绍。在阅读wsgiref源代码之前，一定要对WSGI有一定了解。</p>

<p>WSGI 在 <a href="http://www.python.org/dev/peps/pep-0333/">PEP 333</a> 中描述，但是只靠阅读PEP 333 可能在理解上还是不够深入，所以阅读官方给出的参考实现是很有必要的。阅读完这份源代码后，不仅有利于对WSGI的理解。而且会让你对服务端程序如何对客户端请求有一个直观的理解，从相对底层的socket监听请求，到上层对HTTP请求的处理。</p>

<p>当然，这只是对WSGI的参考实现，目的是为了描述清楚WSGI，而不是真正用在产品中。如果想对Python Web开发中服务器端的实现有更广泛，更深入的理解，需要进一步阅读Python常用框架的源代码。</p>

<h2>wsgiref 源代码分析</h2>

<p>wsgiref 源代码可以在 <a href="https://pypi.python.org/pypi/wsgiref">pypi wsgiref 0.1.2</a> 下载。另外，我在阅读的过程中作了大量注释，包含模块介绍，调用层次关系，demo的运行结果，等 等，还包含了阅读过程中制作的思维导图。GitHub地址<a href="https://github.com/minixalpha/SourceLearning/tree/master/wsgiref-0.1.2">注释版wsgiref</a>。</p>

<h3>结构</h3>

<p><img src="/assets/blog-images/wsgiref.bmp" alt="wsgiref"></p>

<p>上图描述了wsgiref的所有模块及模块间的调用关系，可以看出，wsgiref有以下模块：</p>

<ul>
<li><p>simple_server<br>
这一模块实现了一个简单的 HTTP 服务器，并给出了一个简单的 demo，运行：</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">python simple_server.py
</code></pre></div></li>
</ul>

<p>会启动这个demo，运行一次请求，并把这次请求中涉及到的环境变量在浏览器中显示出来。</p>

<ul>
<li>handlers<br>
simple_server模块将HTTP服务器分成了 Server 部分和Handler部分，前者负责接收请求，后者负责具体的处理，
其中Handler部分主要在handlers中实现。</li>
<li>headers<br>
这一模块主要是为HTTP协议中header部分建立数据结构。</li>
<li>util<br>
这一模块包含了一些工具函数，主要用于对环境变量，URL的处理。</li>
<li>validate<br>
这一模块提供了一个验证工具，可以用于验证你的实现是否符合WSGI标准。</li>
</ul>

<h3>simple_server</h3>

<p><img src="/assets/blog-images/simple_server.bmp" alt="simple_server"></p>

<p>可以看出，simple_server 模块主要有两部分内容</p>

<ul>
<li>应用程序<br>
函数demo_app是应用程序部分</li>
<li>服务器程序<br>
服务器程序主要分成Server 和 Handler两部分，另外还有一个函数 make_server 用来生成一个服务器实例</li>
</ul>

<p>我们先看应用程序部分。</p>

<p>注意：以 <code>M:</code>开始的都是我自己添加的注释，不包含在最初的源代码中。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">def</span> <span class="nf">demo_app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span><span class="n">start_response</span><span class="p">):</span>
    <span class="c"># M: StringIO reads and writes a string buffer (also known as memory files).</span>

    <span class="kn">from</span> <span class="nn">StringIO</span> <span class="kn">import</span> <span class="n">StringIO</span>
    <span class="n">stdout</span> <span class="o">=</span> <span class="n">StringIO</span><span class="p">()</span>
    <span class="k">print</span> <span class="o">&gt;&gt;</span> <span class="n">stdout</span><span class="p">,</span> <span class="s">&quot;Hello world!&quot;</span>
    <span class="k">print</span> <span class="o">&gt;&gt;</span> <span class="n">stdout</span>

    <span class="n">h</span> <span class="o">=</span> <span class="n">environ</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
    <span class="n">h</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">k</span><span class="p">,</span><span class="n">v</span> <span class="ow">in</span> <span class="n">h</span><span class="p">:</span>
        <span class="k">print</span> <span class="o">&gt;&gt;</span> <span class="n">stdout</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span><span class="s">&#39;=&#39;</span><span class="p">,</span><span class="sb">`v`</span>

    <span class="n">start_response</span><span class="p">(</span><span class="s">&quot;200 OK&quot;</span><span class="p">,</span> <span class="p">[(</span><span class="s">&#39;Content-Type&#39;</span><span class="p">,</span><span class="s">&#39;text/plain&#39;</span><span class="p">)])</span>

    <span class="k">return</span> <span class="p">[</span><span class="n">stdout</span><span class="o">.</span><span class="n">getvalue</span><span class="p">()]</span>
</code></pre></div>
<p>这里可以看出，这个 <code>demo_app</code> 是如何展示WSGI中对应用程序的要求的：</p>

<ul>
<li>demo_app 有两个参数</li>
<li>第一个参数 environ是一个字典</li>
<li>第二个参数 start_response是一个可调用函数</li>
<li>demo_app 返回一个可迭代对象</li>
<li>demo<em>app 需要调用 start</em>response</li>
</ul>

<p>另外，可以看出，返回的内容就是环境变量当前的内容，这一点可以运行</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">python simple_server.py
</code></pre></div>
<p>在浏览器中看到的内容，就是上述源代码的for循环中输出的。</p>

<p>这里，使用了 <code>StringIO</code> ，可以看出，所有输出的内容都先存储在其实例中，最后返回的时候一起在可迭代对象中返回。</p>

<p>接下来，我们看服务器程序。</p>

<p>先从 make_server 看起，它是用来生成一个server实例的：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">def</span> <span class="nf">make_server</span><span class="p">(</span>
    <span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">app</span><span class="p">,</span> <span class="n">server_class</span><span class="o">=</span><span class="n">WSGIServer</span><span class="p">,</span> <span class="n">handler_class</span><span class="o">=</span><span class="n">WSGIRequestHandler</span>
<span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Create a new WSGI server listening on `host` and `port` for `app`&quot;&quot;&quot;</span>

    <span class="c"># M: -&gt; HTTPServer.__init__</span>
    <span class="c">#    -&gt; TCPServer.__init__</span>
    <span class="c">#       -&gt; TCPServer.server_bind</span>
    <span class="c">#           -&gt; TCPServer.socket.bind</span>
    <span class="c">#       -&gt; TCPServer.server_activate</span>
    <span class="c">#           -&gt; TCPServer.socket.listen</span>
    <span class="n">server</span> <span class="o">=</span> <span class="n">server_class</span><span class="p">((</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">),</span> <span class="n">handler_class</span><span class="p">)</span>

    <span class="c"># M: conresponding to WSGIRequestHandler.handle()</span>
    <span class="c">#    -&gt; handler.run(self.server.get_app())</span>
    <span class="n">server</span><span class="o">.</span><span class="n">set_app</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">server</span>
</code></pre></div>
<p>虽然代码只有三行，但是可以看出生成一个 server 都需要些什么：</p>

<ul>
<li>(host, port)<br>
主机名和端口号 </li>
<li>handler_class<br>
用于处理请求的handler类</li>
<li>app
服务器程序在处理时，一定会调用我们之前写好的应用程序，这样他们才能配合起来为客户端面服务，所以，你看到了那个 <code>set_app</code> 调用。</li>
</ul>

<p>另外，在注释部分，你可以看到那代码背后都发生了什么。</p>

<p>生成 server 实例时，默认的 server_class 是 WSGIServer，它是HTTPServer的子类，后者又是TCPServer的子类，所以初始化 server 时，会沿着类的继承关系执行下去，最终，生成 server 实例的过程，其实是最底层的 TCPServer 在初始化时，完成了对socket的bind和listen。</p>

<p>后面的 set<em>app 设置了 app，它会在 handler</em>class (默认为WSGIRequestHandler)的handle函数中被取出来，然后交给 handler 的 run 函数运行。</p>

<p>好，现在我们开始介绍Server部分的主要内容，即WSGIServer和WSGIRequestHandler，首先，我们看一下二者的继承体系。</p>

<ul>
<li>WSGIServer</li>
</ul>
<div class="highlight"><pre><code class="text language-text" data-lang="text"># M:
#         +------------+
#         | BaseServer |
#         +------------+
#               |
#               V
#         +------------+
#         | TCPServer  |
#         +------------+
#               |
#               V
#         +------------+
#         | HTTPServer |
#         +------------+
#               |
#               V
#         +------------+
#         | WSGIServer |
#         +------------+
</code></pre></div>
<p>可以看出 WSGIServer 来自 HTTPServer，后者来自 Python 标准库中的BaseHTTPServer模块，更上层的TCPServer和BaseServer来自 Python 标准库中的 SocketServer 模块。</p>

<ul>
<li>WSGIRequestHandler</li>
</ul>
<div class="highlight"><pre><code class="text language-text" data-lang="text"># M:
#         +--------------------+
#         | BaseRequestHandler |
#         +--------------------+
#                   |
#                   V
#         +-----------------------+
#         | StreamRequestHandler  |
#         +-----------------------+
#                   |
#                   V
#         +------------------------+
#         | BaseHTTPRequestHandler |
#         +------------------------+
#                   |
#                   V
#         +--------------------+
#         | WSGIRequestHandler |
#         +--------------------+
</code></pre></div>
<p>可以看出 WSGIRequestHandler 来自 BaseHTTPRequestHandler，后者来自 Python 标准库中的BaseHTTPServer模块，更上层的StreamRequestHandler和BaseRequestHandler来自 Python 标准库中的 SocketServer 模块。</p>

<p>这时候，三个模块之间的层次关系我们可以理清楚了。</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text"># M:
#        +-----------------------------------------------+
#        | simple_server: WSGIServer, WSGIRequestHandler |   
#        |                                               |
#        +-----------------------------------------------+
#                               |
#                               V
#       +----------------------------------------------------+
#       | BaseHTTPServer: HTTPServer, BaseHTTPRequestHandler |
#       +----------------------------------------------------+
#                               |
#                               V
#       +----------------------------------------------------+
#       | SocketServer: TCPServer,BaseSErver;                |
#       | StreamRequestHandler,BaseRequestHandler            |
#       +----------------------------------------------------+
# 
</code></pre></div>
<p>另外，这一模块中还有一个类，叫ServerHandler，它继承自 handlers 模块中的 SimpleHandler，我们再看看它的继承体系：</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text"># M:
#        +-------------+
#        | BaseHandler |  
#        +-------------+
#               |
#               V
#       +----------------+
#       | SimpleHandler  |
#       +----------------+
#               |
#               V
#       +---------------+
#       | ServerHandler |
#       +---------------+
# 
</code></pre></div>
<p>好了，现在这个模块中的继承结构都弄清楚了，现在我们看看他们如何配合起来完成对客户端请求的处理。</p>

<p>首先，回顾simple_server涉及的模块：</p>

<ul>
<li>WSGIServer</li>
<li>WSGIRequestHandler</li>
<li>ServerHandler</li>
<li>demo_app</li>
<li>make_server</li>
</ul>

<p>然后，把大脑清空，想一下要对客户端请求进行处理需要做什么 </p>

<ul>
<li>启动服务器，监听客户端请求</li>
<li>请求来临，处理用户请求</li>
</ul>

<p>我们看看这几个模块是如何配合完成这两个功能的</p>

<p>先看看 simple_server 模块的 <code>main</code> 部分，即执行 </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">python simple_server.py
</code></pre></div>
<p>时执行的内容:</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python">    <span class="n">httpd</span> <span class="o">=</span> <span class="n">make_server</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="mi">8000</span><span class="p">,</span> <span class="n">demo_app</span><span class="p">)</span>
    <span class="n">sa</span> <span class="o">=</span> <span class="n">httpd</span><span class="o">.</span><span class="n">socket</span><span class="o">.</span><span class="n">getsockname</span><span class="p">()</span>
    <span class="k">print</span> <span class="s">&quot;Serving HTTP on&quot;</span><span class="p">,</span> <span class="n">sa</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="s">&quot;port&quot;</span><span class="p">,</span> <span class="n">sa</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s">&quot;...&quot;</span>

    <span class="c"># M: webbrowser provides a high-level interface to allow displaying Web-based documents </span>
    <span class="c"># to users. Under most circumstances</span>
    <span class="kn">import</span> <span class="nn">webbrowser</span>
    <span class="n">webbrowser</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s">&#39;http://localhost:8000/xyz?abc&#39;</span><span class="p">)</span>

    <span class="n">httpd</span><span class="o">.</span><span class="n">handle_request</span><span class="p">()</span>  <span class="c"># serve one request, then exit</span>
</code></pre></div>
<p>可以看出，主要完成的功能是：</p>

<ul>
<li>启动服务器 </li>
<li>模块用户请求</li>
<li>处理用户请求</li>
</ul>

<p>那么，我们主要关心的就是 <code>make_server</code> 和 <code>handle_request</code> 背后都发生了什么。</p>

<ul>
<li>make_server</li>
</ul>

<p><img src="/assets/blog-images/make_server.png" alt="make_server"></p>

<p>上图可以看出函数之间的调用关系，也可以看出 make_server 到 使用 socket 监听用户请求的过程。</p>

<ul>
<li>handle_request</li>
</ul>

<p>handle_request 的过程真正将各个模块联系起来了。</p>

<p><img src="/assets/blog-images/handle_request.png" alt="handle_request"></p>

<p>上图很清楚地说明了 由handle<em>request到demo</em>app的执行过程，把这个模块的各个部分联系起来。相信无需多言了。</p>

<h3>handlers</h3>

<p><img src="/assets/blog-images/handlers.bmp" alt="handlers"></p>

<p>从图中可以看出handler模块中的四部分，它们其实是四个类，从基类到子类依次为：</p>

<ul>
<li>BaseHandler</li>
<li>SimpleHandler</li>
<li>BaseCGIHandler</li>
<li>CGIHandler</li>
</ul>

<p>最主要的实现在 BaseHandler中，其它几个类都是在基类基础上做了简单的实现。BaseHandler是不能直接使用的，因为有几个关键的地方没有实现，SimpleHandler是一个可以使用的简单实现。simple_server中的 ServerHandler类就是这个模块中SimpleHandler的子类。</p>

<p>在 BaseHandler中，最重要的是 <code>run</code> 函数：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">application</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Invoke the application&quot;&quot;&quot;</span>
        <span class="c"># Note to self: don&#39;t move the close()!  Asynchronous servers shouldn&#39;t</span>
        <span class="c"># call close() from finish_response(), so if you close() anywhere but</span>
        <span class="c"># the double-error branch here, you&#39;ll break asynchronous servers by</span>
        <span class="c"># prematurely closing.  Async servers must return from &#39;run()&#39; without</span>
        <span class="c"># closing if there might still be output to iterate over.</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">setup_environ</span><span class="p">()</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">result</span> <span class="o">=</span> <span class="n">application</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">environ</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">start_response</span><span class="p">)</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">finish_response</span><span class="p">()</span>
        <span class="k">except</span><span class="p">:</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="bp">self</span><span class="o">.</span><span class="n">handle_error</span><span class="p">()</span>
            <span class="k">except</span><span class="p">:</span>
                <span class="c"># If we get an error handling an error, just give up already!</span>
                <span class="bp">self</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
                <span class="k">raise</span>   <span class="c"># ...and let the actual server figure it out.</span>
</code></pre></div>
<p>它先设置好环境变量，再调用我们的 <code>demo_app</code>, 并通过 <code>finish_response</code> 将调用结果传送给客户端。如果处理过程遇到错误，转入 <code>handle_error</code> 处理。</p>

<p>此外，BaseHandler中还包含了 WSGI 中多次提到的 start<em>response，start</em>response 在 <code>demo_app</code> 中被调用，我们看看它的实现：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python">    <span class="k">def</span> <span class="nf">start_response</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="p">,</span><span class="n">exc_info</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;&#39;start_response()&#39; callable as specified by PEP 333&quot;&quot;&quot;</span>

        <span class="c"># M:</span>
        <span class="c"># exc_info:</span>
        <span class="c">#    The exc_info argument, if supplied, must be a Python sys.exc_info()</span>
        <span class="c">#    tuple. This argument should be supplied by the application only if</span>
        <span class="c">#    start_response is being called by an error handler.</span>

        <span class="c">#    exc_info is the most recent exception catch in except clause</span>

        <span class="c">#    in error_output:</span>
        <span class="c">#        start_response(</span>
        <span class="c">#             self.error_status,self.error_headers[:],sys.exc_info())</span>

        <span class="c"># headers_sent:</span>
        <span class="c">#    when send_headers is invoked, headers_sent = True</span>
        <span class="c">#    when close is invoked, headers_sent = False</span>

        <span class="k">if</span> <span class="n">exc_info</span><span class="p">:</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers_sent</span><span class="p">:</span>
                    <span class="c"># Re-raise original exception if headers sent</span>
                    <span class="k">raise</span> <span class="n">exc_info</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">exc_info</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">exc_info</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
            <span class="k">finally</span><span class="p">:</span>
                <span class="n">exc_info</span> <span class="o">=</span> <span class="bp">None</span>        <span class="c"># avoid dangling circular ref</span>
        <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">AssertionError</span><span class="p">(</span><span class="s">&quot;Headers already set!&quot;</span><span class="p">)</span>

        <span class="k">assert</span> <span class="nb">type</span><span class="p">(</span><span class="n">status</span><span class="p">)</span> <span class="ow">is</span> <span class="n">StringType</span><span class="p">,</span><span class="s">&quot;Status must be a string&quot;</span>
        <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">status</span><span class="p">)</span><span class="o">&gt;=</span><span class="mi">4</span><span class="p">,</span><span class="s">&quot;Status must be at least 4 characters&quot;</span>
        <span class="k">assert</span> <span class="nb">int</span><span class="p">(</span><span class="n">status</span><span class="p">[:</span><span class="mi">3</span><span class="p">]),</span><span class="s">&quot;Status message must begin w/3-digit code&quot;</span>
        <span class="k">assert</span> <span class="n">status</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="o">==</span><span class="s">&quot; &quot;</span><span class="p">,</span> <span class="s">&quot;Status message must have a space after code&quot;</span>
        <span class="k">if</span> <span class="n">__debug__</span><span class="p">:</span>
            <span class="k">for</span> <span class="n">name</span><span class="p">,</span><span class="n">val</span> <span class="ow">in</span> <span class="n">headers</span><span class="p">:</span>
                <span class="k">assert</span> <span class="nb">type</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="ow">is</span> <span class="n">StringType</span><span class="p">,</span><span class="s">&quot;Header names must be strings&quot;</span>
                <span class="k">assert</span> <span class="nb">type</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="ow">is</span> <span class="n">StringType</span><span class="p">,</span><span class="s">&quot;Header values must be strings&quot;</span>
                <span class="k">assert</span> <span class="ow">not</span> <span class="n">is_hop_by_hop</span><span class="p">(</span><span class="n">name</span><span class="p">),</span><span class="s">&quot;Hop-by-hop headers not allowed&quot;</span>

        <span class="c"># M: set status and headers</span>

        <span class="bp">self</span><span class="o">.</span><span class="n">status</span> <span class="o">=</span> <span class="n">status</span>

        <span class="c"># M:</span>
        <span class="c">#    headers_class is Headers in module headers</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">headers</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers_class</span><span class="p">(</span><span class="n">headers</span><span class="p">)</span>

        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">write</span>
</code></pre></div>
<p>可以看出，它先对参数进行了检查，然后再将headers 存储在成员变量中，这两点 WSGI标准中都有明确说明，需要检查参数，并且这一步只能将 headers存储起来，不能直接发送给客户端。也就是说，这个 <code>start_response</code> 还没有真正 response。</p>

<p>其实刚刚介绍 <code>run</code> 函数的时候已经提到了，真正的 response 在 <code>finish_response</code> 函数中：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python">    <span class="k">def</span> <span class="nf">finish_response</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Send any iterable data, then close self and the iterable</span>

<span class="sd">        Subclasses intended for use in asynchronous servers will</span>
<span class="sd">        want to redefine this method, such that it sets up callbacks</span>
<span class="sd">        in the event loop to iterate over the data, and to call</span>
<span class="sd">        &#39;self.close()&#39; once the response is finished.</span>
<span class="sd">        &quot;&quot;&quot;</span>

        <span class="c"># M:</span>
        <span class="c">#    result_is_file: </span>
        <span class="c">#       True if &#39;self.result&#39; is an instance of &#39;self.wsgi_file_wrapper&#39;</span>
        <span class="c">#    finish_content:</span>
        <span class="c">#       Ensure headers and content have both been sent</span>
        <span class="c">#    close:</span>
        <span class="c">#       Close the iterable (if needed) and reset all instance vars</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">result_is_file</span><span class="p">()</span> <span class="ow">or</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">sendfile</span><span class="p">():</span>
            <span class="k">for</span> <span class="n">data</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">result</span><span class="p">:</span>
                <span class="bp">self</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="c"># send data by self.write</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">finish_content</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div>
<p>另外一个需要注意的地方是错误处理，在 <code>run</code> 函数中通过 <code>try/except</code> 捕获错误，错误处理使用了 <code>handle_error</code> 函数，WSGI中提到，<code>start_response</code> 函数的第三个参数 <code>exc_info</code> 会在错误处理的时候使用，我们来看看它是如何被使用的：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python">    <span class="k">def</span> <span class="nf">handle_error</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Log current error, and send error output to client if possible&quot;&quot;&quot;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">log_exception</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">())</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers_sent</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">error_output</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">environ</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">start_response</span><span class="p">)</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">finish_response</span><span class="p">()</span>
        <span class="c"># XXX else: attempt advanced recovery techniques for HTML or text?</span>

    <span class="k">def</span> <span class="nf">error_output</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;WSGI mini-app to create error output</span>

<span class="sd">        By default, this just uses the &#39;error_status&#39;, &#39;error_headers&#39;,</span>
<span class="sd">        and &#39;error_body&#39; attributes to generate an output page.  It can</span>
<span class="sd">        be overridden in a subclass to dynamically generate diagnostics,</span>
<span class="sd">        choose an appropriate message for the user&#39;s preferred language, etc.</span>

<span class="sd">        Note, however, that it&#39;s not recommended from a security perspective to</span>
<span class="sd">        spit out diagnostics to any old user; ideally, you should have to do</span>
<span class="sd">        something special to enable diagnostic output, which is why we don&#39;t</span>
<span class="sd">        include any here!</span>
<span class="sd">        &quot;&quot;&quot;</span>

        <span class="c"># M:</span>
        <span class="c"># sys.exc_info():</span>
        <span class="c">#    Return information about the most recent exception caught by an except</span>
        <span class="c">#    clause in the current stack frame or in an older stack frame.</span>

        <span class="n">start_response</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">error_status</span><span class="p">,</span><span class="bp">self</span><span class="o">.</span><span class="n">error_headers</span><span class="p">[:],</span><span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">())</span>
        <span class="k">return</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">error_body</span><span class="p">]</span>
</code></pre></div>
<p>看到了吧，<code>handle_error</code> 又调用了 <code>error_output</code> ，后者调用 <code>start_response</code>，并将其第三个参数设置为 <code>sys.exc_info()</code> ，这一点在 WSGI 中也有说明。</p>

<p>好了，这一部分我们就介绍到这里，不再深入过多的细节，毕竟源代码还是要自己亲自阅读的。剩下的三个部分不是核心问题，就是一些数据结构和工具函数，我们简单说一下。</p>

<h3>headers</h3>

<p>headers 模块图</p>

<p>这个模块是对HTTP 响应部分的头部设立的数据结构，实现了一个类似Python 中 dict的数据结构。可以看出，它实现了一些函数来支持一些运算符，例如 <code>__len__</code>, <code>__setitem__</code>, <code>__getitem__</code>, <code>__delitem__</code>, <code>__str__</code>, 另外，还实现了 dict 操作中的 <code>get</code>, <code>keys</code>, <code>values</code>函数。</p>

<h3>util</h3>

<p><img src="/assets/blog-images/util.bmp" alt="util"></p>

<p>这个模块主要就是一些有用的函数，用于处理URL, 环境变量。</p>

<h3>validate</h3>

<p><img src="/assets/blog-images/validate.bmp" alt="validate"></p>

<p>这个模块主要是检查你对WSGI的实现，是否满足标准，包含三个部分：</p>

<ul>
<li>validator</li>
<li>Wrapper</li>
<li>Check</li>
</ul>

<p>validator 调用后面两个部分来完成验证工作，可以看出Check部分对WSGI中规定的各个部分进行了检查。</p>

<p>好了，就介绍到这里，这只是一个总结和导读，再次强调：</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">源代码还是要自己亲自阅读的
</code></pre></div>

      </article>

      <div class="comments">
        
          <div id="disqus_thread"></div>
 <script type="text/javascript">
     /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
     var disqus_shortname = 'minixalpha'; // required: replace example with your forum shortname

     /* * * DON'T EDIT BELOW THIS LINE * * */
     (function() {
         var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
         dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
         (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
     })();
 </script>
 <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
    

        
      </div>


      <footer>
        Copyright (c) minixalpha 2014
      </footer>

    </div>
  </body>
</html>
